<?php
/**
 * db_mysqli.class.php
 * 
 * db_mysqli数据库驱动,完成对mysql数据库的操作
 * 
 * @author lsl <470663151@qq.com> 
 * @copyright Copyright (c) 2012 lsl8966 Software Studio
 * @link http://www.cnblogs.com/lsl8966
 * @version $Id: AutoLoad.class.php 1.0 2012-01-18 22:48:01Z lsl $
 * @package core
 * @since 1.0
 */

if (!defined('IN_PDK')){
     exit();
    }

class db_mysqli extends Base{
    private  $db_name;
    /**
     * 单例模式实例化本类
     * 
     * @var object 
     */
     public static $_instance;
    
    /**
     * 数据库连接ID
     * 
     * @var object 
     */
     public static $dbLink = array();
     public static $dbConfig = array();

    /**
     * 事务处理开启状态
     * 
     * @var boolean 
     */
     public $Transactions;
    
    
    /**
     * 构造函数

     * 用于初始化运行环境,或对基本变量进行赋值
     * 
     * @access public 
     * @param array $params 数据库连接参数,如主机名,数据库用户名,密码等
     * @return boolean 
     */
     public function __construct($params = array()){
         $this->db_name = $params['dbname'];
       self::$dbConfig[$params['dbname']]  = $params;
       $this->connect($params);
     }

    private function getConnect(){
        $this->connect(self::$dbConfig[$this->db_name]);
        return self::$dbLink[$this->db_name];
    }

    private function connect($params  = array()){
        // 检测参数信息是否完整
        if (!$params['host'] || !$params['username'] || !$params['dbname']){
            Controller :: halt('Database Server:HostName or UserName or Password or Database Name is error in the config file!');
        }
        // 实例化mysql连接ID
        if(isset(self::$dbLink[$this->db_name]) && self::$dbLink[$this->db_name]){
            return true;
        }else{
            self::$dbLink[$this->db_name] = $params['port'] ? @new mysqli($params['host'], $params['username'], $params['password'], $params['dbname'], $params['port']) : @new mysqli($params['host'], $params['username'], $params['password'], $params['dbname']);
            if (mysqli_connect_errno()){
                // 当调试模式开启时(PDK_DEBUG为true时).
                if (PDK_DEBUG === true){
                    Controller :: halt('Mysql Server connect fail.<br/>Error Message:' . mysqli_connect_error() . '<br/>Error Code:' . mysqli_connect_errno(), 'Warning');
                }else{
                    Log :: write('Mysql Server connect fail. Error Code:' . mysqli_connect_errno() . ' Error Message:' . mysqli_connect_error(), 'Warning');
                }
            }else{
                // 设置数据库编码
                self::$dbLink[$this->db_name] -> query("SET NAMES {$params['charset']}");
                $sqlVersion = $this -> getServerInfo();
                if (version_compare($sqlVersion, '5.0.2', '>=')){
                    self::$dbLink[$this->db_name] -> query("SET SESSION SQL_MODE=''");
                }
            }
        }
        return true;
    }

    /**
     * 执行SQL语句
     * 
     * SQL语句执行函数.
     * 
     * @access public 
     * @param string $sql SQL语句内容
     * @return mixed 
     */
     public function query($sql){
		 if(defined('LOG_SQL') && LOG_SQL){
			Log :: write($sql, 'Info', 'info/sql_' . date('Y_m_d', $_SERVER['REQUEST_TIME']));
		 }
         if($_GET['is_debug'] == 1){
             var_dump($sql);
         }else if($_GET['is_debug'] == 2){
            echo "<script>console.info(".json_encode($sql).");</script>";
         }else{
             //什么都不做
         }
         // 参数分析
        if (!$sql){
             return false;
         }
         // 获取执行结果
        $result = $this->getConnect()-> query($sql);

		// 获取当前运行的controller及action名称
		$controllerId = pdk ::getControllerName();
		$actionId = pdk ::getActionName();
		if ($result == false){
			Log :: write('[' . $controllerId . '][' . $actionId . '] SQL execute failed :' . $sql . ' Error Code:' . $this -> errno() . 'Error Message:' . $this -> error());
		}
         // 日志操作,当调试模式开启时,将所执行过的SQL写入SQL跟踪日志文件,便于DBA进行MYSQL优化.若调试模式关闭,当SQL语句执行错误时写入日志文件
        else if (defined('LOG_SQL') && LOG_SQL){
            $sqlLogFile = 'trace/sql_' . date('Y_m_d', $_SERVER['REQUEST_TIME']);
            Log :: write('[' . $controllerId . '][' . $actionId . ']:' . $sql, 'Normal', $sqlLogFile);
        }

        return $result;
    }
    
    /**
     * 获取mysql数据库服务器信息
     * 
     * @access public 
     * @return string 
     */
     public function getServerInfo(){
         // 当没有mysql连接时
        if (!self::$dbLink[$this->db_name]){
             return false;
             }
        
         return $this->getConnect()-> server_version;
         }
    
    /**
     * 获取mysql错误描述信息
     * 
     * @access public 
     * @return string 
     */
     public function error(){
        
         return $this->getConnect()-> error;
         }
    
    /**
     * 获取mysql错误信息代码
     * 
     * @access public 
     * @return int 
     */
     public function errno(){
        
         return $this->getConnect()-> errno;
     }
    
    /**
     * 通过一个SQL语句获取一行信息(字段型)
     * 
     * @access public 
     * @param string $sql SQL语句内容
     * @return mixed 
     */
     public function fetchRow($sql){
        // 参数分析
        if (!$sql){
         return false;
        }
         if(PDK_DB_CACHE){
             $cache_model = cache::factory(CACHE_TYPE);
             $cache_model->setGroupTag($this->db_name. DIRECTORY_SEPARATOR.__FUNCTION__);
             $cache_data = $this->getCache($cache_model,$sql);
         }
        if(PDK_DB_CACHE && is_array($cache_data)){
            unset($cache_model);
            return $cache_data;
        }else{
            // 执行SQL语句
            $result = $this -> query($sql);
            if(!$result){
                return false;
            }
            $rows = $result -> fetch_assoc();
            // 清空不必要的内存占用
            $result -> free();
            if(PDK_DB_CACHE){
                $this->setCache($cache_model,$rows,$sql);
                unset($cache_model);
            }
            return $rows;
        }
     }
    
    /**
     * 通过一个SQL语句获取全部信息(字段型)
     * 
     * @access public 
     * @param string $sql SQL语句
     * @return array 
     */
     public function getArray($sql){
         // 参数分析
        if (!$sql){
             return false;
         }
         if(PDK_DB_CACHE){
             $cache_model = cache::factory(CACHE_TYPE);
             $cache_model->setGroupTag($this->db_name. DIRECTORY_SEPARATOR.__FUNCTION__);
             $cache_data = $this->getCache($cache_model,$sql);
         }
         if(PDK_DB_CACHE && is_array($cache_data)){
             unset($cache_model);
             return $cache_data;
         }else{
             // 执行SQL语句.
             $result = $this -> query($sql);
             if (!$result){
                 return false;
             }

             $myrow = array();
             while ($row = $result -> fetch_assoc()){
                 $myrow[] = $row;
             }
             $result -> free();
             if(PDK_DB_CACHE && count($myrow) < 500){
                 $this->setCache($cache_model,$myrow,$sql);
             }
             unset($cache_model);
             return $myrow;
         }
     }
    
    /**
     * 获取insert_id
     * 
     * @access public 
     * @return int 
     */
     public function insertId(){
        
         return ($id = $this->getConnect()-> insert_id) >= 0 ? $id :$this -> query("SELECT last_insert_id()") -> fetch_row();
         }
    
    /**
     * 开启事务处理
     * 
     * @access public 
     * @return boolean 
     */
     public function startTrans(){
        
         if ($this -> Transactions == false){
             $this->getConnect()-> autocommit(false);
             $this -> Transactions = true;
             }
        
         return true;
         }
    
    /**
     * 提交事务处理
     * 
     * @access public 
     * @return boolean 
     */
     public function commit(){
        
         if($this -> Transactions == true){
             $result = $this->getConnect()-> commit();
            
             if ($result){
                 $this->getConnect()-> autocommit(true);
                 $this -> Transactions = false;
                 }else{
                 // 获取当前运行的controller及action名称
                $controllerId = pdk ::getControllerName();
                 $actionId = pdk ::getActionName();
                
                 if (PDK_DEBUG === true){
                     Controller :: halt('[' . $controllerId . '][' . $actionId . '] SQL Commit failed <br/>Error Message:' . $this -> error() . '<br/>Error Code:' . $this -> errno());
                     }else{
                     Log :: write('[' . $controllerId . '][' . $actionId . '] SQL Commit failed. Error Code:' . $this -> errno() . ' Error Message:' . $this -> error());
                     Controller :: showMessage('Mysql Server SQL execute failed!');
                     }
                 }
             }
        
         return true;
         }
    
    /**
     * 事务回滚
     * 
     * @access public 
     * @return boolean 
     */
     public function rollback(){
        
         if ($this -> Transactions == true){
             $result = $this->getConnect()-> rollback();
            
             if ($result){
                 $this->getConnect()-> autocommit(true);
                 $this -> Transactions = false;
                 }else{
                 // 获取当前运行的controller及action名称
                $controllerId = pdk ::getControllerName();
                 $actionId = pdk ::getActionName();
                
                 if (PDK_DEBUG === true){
                     Controller :: halt('[' . $controllerId . '][' . $actionId . '] SQL RollBack failed! <br/>Error Message:' . $this -> error() . '<br/>Error Code:' . $this -> errno());
                     }else{
                     Log :: write('[' . $controllerId . '][' . $actionId . '] SQL RollBack failed! Error Code:' . $this -> errno() . ' Error Message:' . $this -> error());
                     Controller :: showMessage('Database SQL execute failed!');
                     }
                 }
             }
        
         return true;
         }
    
    /**
     * 转义字符
     * 
     * @access public 
     * @param string $string 待转义的字符串
     * @return string 
     */
     public function escapeString($string = null){

         // 参数分析
        if (is_null($string)){
             return false;
             }

         return $this->getConnect()-> real_escape_string($string);
         }
    
    /**
     * 析构函数
     * 
     * @access public 
     * @return void 
     */
     public function __destruct(){
        
         // 关闭数据库连接
        if (self::$dbLink[$this->db_name]){
             @$this->getConnect()-> close();
                unset(self::$dbLink[$this->db_name]);
             }
         }

    public function close(){
        // 关闭数据库连接
        if (self::$dbLink[$this->db_name]){
            @$this->getConnect()-> close();
            unset(self::$dbLink[$this->db_name]);
        }
    }

    private function getCacheKey($key,$is_md5 = false){
        $key = trim($key);
        $key = str_replace(' ','',$key);
        $key = str_replace('`','_',$key);
        $key = str_replace('*','_',$key);
        $key = str_replace(',','_',$key);
        $key = str_replace('\'','_',$key);
        $key = str_replace('"','_',$key);
        if(strlen($key)>=250 || $is_md5 === true){
            $key = md5($key);
        }
        return strtolower($key);
    }

    private function setCache($cache_model,$data,$sql){
        $id = $this->getCacheKey($this->db_name.$sql);
        $obj = explode('where',strtolower($sql));
        $key = $obj[0];
        $key = $this->getCacheKey($key);
        $list = $cache_model->get($key);
        $list[$id] = $data;
        return $cache_model->set($key,$list,300);
    }

    private function getCache($cache_model,$sql){
        $id = $this->getCacheKey($this->db_name.$sql);
        $obj = explode('where',strtolower($sql));
        $key = trim($obj[0]);
        $key = $this->getCacheKey($key);
        $list = $cache_model->get($key);
        return $list[$id];
    }

    /**
     * 单例模式
     * 
     * 用于本类的单例模式(singleton)实例化
     * 
     * @access public 
     * @param array $params 数据库连接参数,如数据库服务器名,用户名,密码等
     * @return object 
     */
     public static function getInstance($params){
        
         if (!self :: $_instance){
             self :: $_instance = new self($params);
             }
        
         return self :: $_instance;
         }
    }
